/*
 * adaptadorAnillo.cpp
 *
 *  Created on: Dec 26, 2013
 *      Author: m
 */

#include <string>
#include "anilloComun.h"
#include "CPrinter.h"
#include "StringUtils.h"
#include "ColaRemotaTransmisor.h"
#include "ColaRemotaReceptorDirecto.h"
#include "../IPC/ProcessAux.h"

using ipc::CPrinter;

#define ID_ANILLO_ARG 1
#define CHANNEL_BASENAME "anillo"

int main ( int argc, char* argv[] )
{
    int idAnillo;
    int brokerId;
    int my_uuid;
    int siguienteBrokerId;
    estadoAnillo_t estado;

    CPrinter::setHeader( "adaptadorAnillo" );

    if ( argc != 2 )
    {
        CPrinter::printErr( "ERROR: Cantidad de argumentos invalida." );
        CPrinter::printErr( "USO: %s [id_anillo]", argv[0] );
        exit( 1 );
    }

    // Parsear argumentos
    std::string idAnillo_str = argv[ ID_ANILLO_ARG ];
    idAnillo = StringUtils::StringToNumber<int>( argv[ ID_ANILLO_ARG ] );

    CPrinter::setHeader( "adaptadorAnillo", idAnillo );

    // Cargar la configuración de un archivo
    ArchivoIni config( "config.ini" );

    /*
     * brokerId
     * AnilloUUID
     * AnilloSiguiente
     */

    // TODO: Si es necesario serializar la creacion de cada anillo con un mutex
    //        OJO! el mutex debe ser tomado por el mismo proceso en todos los
    //        brokers!! (sino se pueden levantar muchos forwarders entre los brokers)

    std::string brokerId_str;
    std::string uuid_str;
    std::string siguienteBrokerId_str;

    brokerId_str = config.getValue( "brokerId" );
    uuid_str = config.getValue( "AnilloUUID" );
    siguienteBrokerId_str = config.getValue( "AnilloSiguiente" );

    brokerId = StringUtils::StringToNumber<int>( brokerId_str );
    my_uuid = StringUtils::StringToNumber<int>( uuid_str );
    siguienteBrokerId = StringUtils::StringToNumber<int>( siguienteBrokerId_str );

    std::string nombreCanalEntrada ( CHANNEL_BASENAME + idAnillo_str + "." + brokerId_str );
    std::string nombreCanalSalida  ( CHANNEL_BASENAME + idAnillo_str + "." + siguienteBrokerId_str );

    ColaRemotaTransmisor transmisor;
    ColaRemotaReceptorDirecto receptor( nombreCanalEntrada );

    CPrinter::printOut("INICIADO");

    // Estado inicial: DESCONOCIDO
    estado = DESCONOCIDO;

    // Envio mi uuid
    //CPrinter::printOut("DEBUG: Enviando mi uuid a %s", nombreCanalSalida.c_str() );
    transmisor.enviarMensaje<int>( nombreCanalSalida, my_uuid );

    // Luego espero uuids de los otros participantes del anillo
    while( true )
    {
        //CPrinter::printOut("DEBUG: Esperando uuid");
        int uuid = receptor.recibirMensaje<int>();

        if ( uuid == 0 )
        {
            // Termino la elección de lider
            if ( estado != LIDER )
            {
                //CPrinter::printOut("DEBUG: No lider esperando fin eleccion lider");
                transmisor.enviarMensaje<int>( nombreCanalSalida, 0 );
            }
            break;
        }

        // Si el uuid es menor al mio lo descarto y sigo de largo
        if ( uuid < my_uuid )
        {
            continue;
        }

        // Si es mayor al mio lo envio al siguiente participante del anillo
        // Además se que no soy el lider del anillo
        if ( uuid > my_uuid )
        {
            estado = NO_LIDER;
            transmisor.enviarMensaje<int>( nombreCanalSalida, uuid );
            continue;
        }

        // Si es igual a mi uuid significa que soy el lider!
        // Mando un mensaje avisando que ya se eligio un lider
        if ( uuid == my_uuid )
        {
            estado = LIDER;
            transmisor.enviarMensaje<int>( nombreCanalSalida, 0 );
        }
    }

    Argv args;

    switch ( estado )
    {
    case LIDER:
        // Lanzo el proceso que crea el token
        // Lanzo el proceso que gestiona el token
        CPrinter::printOut("Soy lider!");

        args.pushArg( "lider" );

        ProcessAux::launchProcess(
            std::string( "./gestorToken" ) + argv[ ID_ANILLO_ARG ], 1, args );

        break;

    case NO_LIDER:
        // Lanzo el proceso que gestiona el token
        CPrinter::printOut("No soy lider");

        args.pushArg( "no-lider" );

        ProcessAux::launchProcess(
            std::string( "./gestorToken" ) + argv[ ID_ANILLO_ARG ], 1, args );

        break;

    case DESCONOCIDO:
        CPrinter::printErr( "ERROR: Fallo al identificar lider del anillo" );
        exit( 1 );
    }

    return 0;
}
